﻿using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Security;
using System.Security.Permissions;
using System.Reflection;
using SharePointListSecurity.Model;

namespace SharePointListSecurity.WebControls
{
    public class SecureListFieldIterator : ListFieldIterator
    {
        #region members
        private List<SPField> readOnlyFields;
        private List<SPField> hiddenFields;
        #endregion

        #region Overridden Methods
        /// <summary>
        /// overridden method 
        /// this method initialize the hidden fields and the readonly fields lists 
        /// for the current user
        /// </summary>
        [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (FeatureIds.IsFeatureActivated())
            {
                SPControlMode controleMode = SPContext.Current.FormContext.FormMode;
                var listSecurityService = new SharepointListSecurityService();
                hiddenFields = listSecurityService.GetHiddenFields(SPContext.Current.List, controleMode);
                if (controleMode == SPControlMode.Edit)
                    readOnlyFields = listSecurityService.GetReadOnlyFields(SPContext.Current.List);
            }
        }
        protected override void OnLoad(EventArgs e)
        {
            if (FeatureIds.IsFeatureActivated())
            {
                if (Page != null && Page.IsPostBack)
                {
                    Page.Validate();
                }
            }
        }
        /// <summary>
        /// this function exclude the field if it is a member of the hidden fields list
        /// else the field the base function will be executed
        /// </summary>
        protected override bool IsFieldExcluded(Microsoft.SharePoint.SPField field)
        {
            if (FeatureIds.IsFeatureActivated())
            {
                return hiddenFields != null && hiddenFields.Count > 0 && hiddenFields.Exists(p => p.Id == field.Id)
                       ||
                       base.IsFieldExcluded(field);
            }
            return base.IsFieldExcluded(field);
        }

        /// <summary>
        /// this function creates the control of each field
        /// the field will be tested if it's excluded or not 
        /// if not it will be added to the control 
        /// </summary>
        protected override void CreateChildControls()
        {
            if (FeatureIds.IsFeatureActivated())
            {
                this.Controls.Clear();
                if (this.ControlTemplate == null)
                {
                    throw new ArgumentException("Could not find ListFieldIterator control template.");
                }
                for (int i = 0; i < base.Fields.Count; i++)
                {
                    SPField field = base.Fields[i];
                    String fieldName = field.InternalName;

                    if ((!this.IsFieldExcluded(field)))
                    {
                        var templateContainer = new TemplateContainer();
                        this.Controls.Add(templateContainer);
                        var controlMode = GetControlMode(readOnlyFields, field);
                        SetControlMode(templateContainer, controlMode);
                        SetFieldName(templateContainer, field);
                        this.ControlTemplate.InstantiateIn(templateContainer);
                    }
                }
            }
            else
                base.CreateChildControls();
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// this function returns the control of a field
        /// if the field is a member of the readonly fields 
        /// it returns Display as Control
        /// else it returns the current Control
        /// </summary>
        private SPControlMode GetControlMode(List<SPField> readOnlyFields, SPField field)
        {
            return readOnlyFields != null && readOnlyFields.Count > 0
                && readOnlyFields.Exists(p => p.Id == field.Id)
                   ? SPControlMode.Display
                   : SPContext.Current.FormContext.FormMode;
        }
        /// <summary>
        /// this a reflection that set the control mode
        /// this method is used because the control is declared internal
        /// </summary>
        private static void SetControlMode(TemplateContainer templateContainer, SPControlMode controlMode)
        {
            try
            {
                templateContainer.GetType().GetProperty("ControlMode", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(templateContainer, controlMode, null);
            }
            catch //(ReflectionTypeLoadException ex)
            {
            }
        }
        /// <summary>
        /// this a reflection that set the FieldName
        /// this method is used because the field is declared internal
        /// </summary>
        private static void SetFieldName(TemplateContainer templateContainer, SPField field)
        {
            try
            {
                templateContainer.GetType().GetProperty("FieldName", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(templateContainer, field.InternalName, null);
            }
            catch //(ReflectionTypeLoadException ex)
            {
            }
        }

        #endregion
    }
}